package fcnet

import (
	"encoding/json"
	"reflect"
	"sync"
	"time"

	"gitee.com/fcsvr/fc/fclog"
)

type ItfHandle interface {
	SetArgvi(argvi any)
	GetArgvi() any
	PreHandle(r *Req)
	CurHandle(r *Req)
	PostHandle(r *Req)
}

type BaseHandle struct {
	Argvi any
}

func (this *BaseHandle) SetArgvi(argvi any) {
	this.Argvi = argvi
}
func (this *BaseHandle) GetArgvi() any {
	return this.Argvi
}
func (this *BaseHandle) GetArg() any {
	return nil
}
func (this *BaseHandle) PreHandle(r *Req) {
}
func (this *BaseHandle) CurHandle(r *Req) {
}
func (this *BaseHandle) PostHandle(r *Req) {
}

type MsgHandleMgr struct {
	HandleMap     sync.Map
	InitHandle    ItfHandle
	FiniHandle    ItfHandle
	WorkerPoolLen uint
	WorkerTaskLen uint
	TaskQueue     []chan *Req

	BenchTm float64
	BenchCt int
}

func NewMsgHandleMgr() *MsgHandleMgr {
	r := &MsgHandleMgr{
		InitHandle:    &BaseHandle{},
		FiniHandle:    &BaseHandle{},
		WorkerPoolLen: SvrMgr.ConfigSvr().WorkerPoolLen,
		WorkerTaskLen: SvrMgr.ConfigSvr().WorkerTaskLen,
		TaskQueue:     make([]chan *Req, SvrMgr.ConfigSvr().WorkerTaskLen),
	}
	return r
}

func (this *MsgHandleMgr) AddInitHandle(msgid uint32, handle ItfHandle) {
	this.InitHandle = handle
}

func (this *MsgHandleMgr) AddFiniHandle(msgid uint32, handle ItfHandle) {
	this.FiniHandle = handle
}

func (this *MsgHandleMgr) AddHandle(msgid uint32, handle ItfHandle, arg any) {
	argTyp := reflect.TypeOf(arg)
	fclog.Info("MsgHandle DoMsgHandle: msg argTyp = %+v", argTyp)
	argvi := reflect.New(argTyp.Elem()).Interface()
	if argTyp.Kind() != reflect.Ptr {
		argvi = reflect.New(argTyp).Elem().Addr().Interface()
	}
	handle.SetArgvi(argvi)

	if _, ok := this.HandleMap.LoadOrStore(msgid, handle); ok {
		panic("MsgHandleMgr AddHandle msgid already exist!")
	}
	fclog.Info("MsgHandleMgr AddHandle msgid = %d\n", msgid)
}

func (this *MsgHandleMgr) GetHandle(msgid uint32) ItfHandle {
	handle, ok := this.HandleMap.Load(msgid)
	if !ok {
		return nil
	}
	return handle.(ItfHandle)
}

func (this *MsgHandleMgr) DoInitHandle(r *Req) {
	this.InitHandle.CurHandle(r)
}

func (this *MsgHandleMgr) DoFiniHandle(r *Req) {
	this.FiniHandle.CurHandle(r)
}

func (this *MsgHandleMgr) DoMsgHandle(r *Req) {
	startTm := time.Now()

	msgHandle := this.GetHandle(r.GetMsgID())
	if msgHandle == nil {
		fclog.Error("MsgHandleMgr no handle, msgid = %d\n", r.GetMsgID())
		return
	}
	argvi := msgHandle.GetArgvi()
	if err := json.Unmarshal(r.GetMsgBody(), argvi); err != nil {
		fclog.Error("MsgHandleMgr unmarshal msgid = %d, err = %v\n", r.GetMsgID(), err)
		return
	}

	msgHandle.PreHandle(r)
	msgHandle.CurHandle(r)
	msgHandle.PostHandle(r)

	this.BenchTm += time.Since(startTm).Seconds()
	this.BenchCt++
	fclog.Test("MsgHandleMgr DoMsgHandle: average = %d, benchtm = %f", this.BenchCt, this.BenchTm/float64(this.BenchCt))
}

func (this *MsgHandleMgr) StartOneWorker(id int, queue chan *Req) {
	fclog.Info("MsgHandleMgr StartOneWorker id = %d", id)
	for {
		select {
		case handle := <-queue:
			this.DoMsgHandle(handle)
		}
	}
}

func (this *MsgHandleMgr) StartWorkerPool() {
	for i := 0; i < int(this.WorkerPoolLen); i++ {
		this.TaskQueue[i] = make(chan *Req, this.WorkerTaskLen)
		go this.StartOneWorker(i, this.TaskQueue[i])
	}
}

func (this *MsgHandleMgr) SendMsgToWorker(h *Req) {
	workerID := h.Conn.GetConnID() % int(this.WorkerPoolLen)
	this.TaskQueue[workerID] <- h
}
